package css590.dataminer.core.svnminer;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;

import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.BasicAuthenticationManager;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

import css590.dataminer.core.Miner;
import css590.dataminer.core.UserNamePasswordCredential;
import css590.dataminer.core.contracts.IDataFormatter;

/**
 * @author aj366c
 *
 */
/**
 * @author aj366c
 *
 */
@SuppressWarnings("unused")
public class SvnMiner extends Miner {
	
	private SVNRepository repository;
	private String internalDbFileName = "dataminerDb.xml";

	public SvnMiner(String url, UserNamePasswordCredential credential,
			IDataFormatter formatter) {
		super(formatter);
		super.setCredential(credential);
		super.setUrlServer(url);
		DAVRepositoryFactory.setup();
		ISVNAuthenticationManager authManager = new BasicAuthenticationManager(
				this.getCredential().getUserName() 
				, this.getCredential().getPassword());


		try {
			SVNURL svnURL = SVNURL.parseURIEncoded(this.getUrlServer());
			repository = SVNRepositoryFactory.create(svnURL);
			repository.setAuthenticationManager(authManager);
		} catch (SVNException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	
	
	/**
	 * traversing the Xml Document to find the element with a specific Attribute value
	 * NOTE: I tried to use getElementByID but it didnt' work. Somehow, the setIdAttribute doesn't work.
	 * 
	 * @param root
	 * @param attrName
	 * @param attrValue
	 * @return
	 */
	private Element getNodeWithAttribute(Node root, String attrName, String attrValue) {     
		NodeList nl = root.getChildNodes();    
		for (int i = 0; i < nl.getLength(); i++) {         
			Node n = nl.item(i);         
			if (n instanceof Element) {             
				Element el = (Element) n;             
				if (el.getAttribute(attrName).equals(attrValue)) {                 
					return el;             
				}
				else{        
					el =  getNodeWithAttribute(n, attrName, attrValue); 
				}
			}
		}
		return null;
	}
	
	/**
	 * Get the last revision number
	 * @return Revision number
	 */
	private synchronized long getRevision(){
		
		File initDbFile = new File(internalDbFileName);
		if (!initDbFile.exists()) {
			return 0;
		}

		Document document;
		DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder dBuilder;
		try {
			dBuilder = dbFactory.newDocumentBuilder();
			//--continue get the revision number
			try {
				document = dBuilder.parse(initDbFile);
				Element el = this.getNodeWithAttribute(document, "ID", this.getUrlServer());
				if(el != null){
					return Integer.parseInt(el.getTextContent());
				}
			} catch (SAXException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return 0;
	}
	
	/**
	 * set the revision number to xml document
	 * @param revisionNumber
	 */
	/**
	 * @param revisionNumber
	 */
	private synchronized void setRevision(long revisionNumber) {
		File initDbFile = new File(internalDbFileName);
		Document document = null;
		DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();

		try {
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			if(!initDbFile.exists()){
				document = dBuilder.newDocument();
			}
			else {
				try {
					document = dBuilder.parse(initDbFile);
				} catch (SAXException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

			Node lastNode = document.getFirstChild();
			Element el = this.getNodeWithAttribute(document, "ID", this.getUrlServer());
			if(el == null){
				el = document.createElement("SVNRepository");
				el.setAttribute("ID", this.getUrlServer());
				lastNode.appendChild(el);
			}
			el.setTextContent(Long.toString(revisionNumber));
			this.saveDb(document);
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
	}

	/**
	 * saving the xml document to a file folder
	 * @param document
	 */
	private synchronized void saveDb(Document document) {
		TransformerFactory transformerFactory = TransformerFactory.newInstance();
		Transformer transformer;
		try {
			transformer = transformerFactory.newTransformer();
			DOMSource source = new DOMSource(document);
			StreamResult result = new StreamResult(new File(internalDbFileName));
			try {
				transformer.transform(source, result);
			} catch (TransformerException e) {
				e.printStackTrace();
			}
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		}

	}

	@SuppressWarnings("rawtypes")
	@Override
	public void execute() {
		// TODO Auto-generated method stub
		try {
	        long startRevision = this.getRevision();
	        long endRevision = repository.getLatestRevision(); //HEAD (the latest) revision
			//--no changes
            if(startRevision == endRevision)
            	return;
            //--
            List<Object> logs =  new ArrayList<Object>();
            Collection logEntries = repository.log(new String[]{""}, null, startRevision, endRevision, true, true);
            
            for ( Iterator entries = logEntries.iterator( ); entries.hasNext( ); ) {
                SVNLogEntry logEntry = ( SVNLogEntry ) entries.next( );
                logs.add(logEntry);
                System.out.println( "---------------------------------------------" );
                System.out.println ("revision: " + logEntry.getRevision( ) );
                System.out.println( "author: " + logEntry.getAuthor( ) );
                System.out.println( "date: " + logEntry.getDate( ) );
                System.out.println( "log message: " + logEntry.getMessage( ) );

            }
            this.setRevision(endRevision);
            //--push the data to Formatter component
			this.getFormatter().transform(logs);
			//--
		} 
		catch (SVNException ex) {
			Logger.getLogger(SvnMiner.class.getName()).log(Level.SEVERE, null, ex);
		}
	}

}
